﻿#ifndef ___PCLIB_S_HOTKEY___
#define ___PCLIB_S_HOTKEY___


#include "../m_core/m_core.hpp"


// 注册热键消息 wparam=热键id，lparam=热键名称
#define PC_WM_ADDHOTKEY     (WM_USER+2781)
#define PC_WM_DELHOTKEY     (WM_USER+2782)
#define PC_WM_EXITHOTKEY    (WM_USER+2783)

/**
*@brief     热键信息类
*           这个类记录了热键对应的信息
*/
class CPCHotKeyInfo
{
public:
    typedef std::function<void(std::string)> PCHotKeyCallBack;
public:
    explicit CPCHotKeyInfo()
        :m_mods(0)
        , m_key(0)
    {
    }

    /**
    *@brief     设置热键
    *@param     keyname    [in]    热键名称，如ALT+Q CONTROL+1 SHIFT+ALT+Z WIN+L
    *@param     callback   [in]    热键触发的回调函数
    *@return    是否成功
    */
    CPCResult<> Init(const char* name, PCHotKeyCallBack func)
    {
        CPCResult<> result;
        if (name == NULL || name[0] == 0)
            return result.SetFail("param empty");
        m_name = name;
        m_func = func;
#if defined(PCOS_WIN)
        auto keyNameList = CPCStrUtil::PCStrSplit(name, "+");
        if (keyNameList.size() == 2)
        {
            auto ret_mod = ModString2NativeCode(keyNameList[0].c_str());
            if (!ret_mod)
            {
                return result.SetFail("ModString2NativeCode fail:%s",ret_mod.ErrDesc());
            }
            auto ret_key = KeyString2NativeCode(keyNameList[1].c_str());
            if (!ret_key)
            {
                return result.SetFail("KeyString2NativeCode fail:%s",ret_key.ErrDesc());
            }

            m_mods = ret_mod.Get();
            m_key = ret_key.Get();
        }
        else if (keyNameList.size() == 3)
        {
            auto ret_mod1 = ModString2NativeCode(keyNameList[0].c_str());
            if (!ret_mod1)
            {
                return result.SetFail("ModString2NativeCode 1 fail:%s",ret_mod1.ErrDesc());
            }
            auto ret_mod2 = ModString2NativeCode(keyNameList[1].c_str());
            if (!ret_mod2)
            {
                return result.SetFail("ModString2NativeCode 2 fail:%s",ret_mod2.ErrDesc());
            }
            auto ret_key = KeyString2NativeCode(keyNameList[2].c_str());
            if (!ret_key)
            {
                return result.SetFail("KeyString2NativeCode fail:%s",ret_key.ErrDesc());
            }

            m_mods = ret_mod1.Get() | ret_mod2.Get();
            m_key = ret_key.Get();
        }
        else
        {
            return result.SetFail("key list count(%d) invalid, only accept 2 or 3 .", (int)keyNameList.size());
        }
#elif defined(PCOS_ANDROID)
        return result.SetFail("unsupported.");
#elif defined(PCOS_LINUX)
        return result.SetFail("unsupported.");
#else
        return result.SetFail("unsupported.");
#endif
        return result;
    }

    /**
    *@brief     字符串热键mod转化为整数
    *@param     name    [in]    热键，如ALT CONTROL
    *@return    是否成功,IntResult()为结果
    */
    CPCResult<int> ModString2NativeCode(const char* name)
    {
        CPCResult<int> result;
        int mod = 0;
#if defined(PCOS_WIN)
        if (strcasecmp(name, "SHIFT") == 0)
        {
            mod = MOD_SHIFT;
        }
        else if (strcasecmp(name, "ALT") == 0)
        {
            mod = MOD_ALT;
        }
        else if (strcasecmp(name, "CONTROL") == 0)
        {
            mod = MOD_CONTROL;
        }
        else if (strcasecmp(name, "WIN") == 0)
        {
            mod = MOD_WIN;
        }
        else
        {
            return result.SetFail("invalid mod key:%s", name);
        }
#else
        return result.SetFail("unsupported.");
#endif
        return result.SetSucc(mod);
    }

    /**
    *@brief     字符串热键key转化为整数
    *@param     name    [in]    热键，如A 1 SPACE
    *@return    是否成功,IntResult()为结果
    */
    CPCResult<int> KeyString2NativeCode(const char* name)
    {
        CPCResult<int> result;
        if (strlen(name) == 1 &&
            (name[0] >= '0' && name[0] <= '9') ||
            (name[0] >= 'A' && name[0] <= 'Z'))
        {
            return result.SetSucc(name[0]);
        }
        int key = 0;
#if defined(PCOS_WIN)
        if (strcasecmp(name, "SPACE") == 0)       key = VK_ESCAPE;
        else if (strcasecmp(name, "TAB") == 0)    key = VK_TAB;
        else if (strcasecmp(name, "BACK") == 0)   key = VK_BACK;
        else if (strcasecmp(name, "RETURN") == 0) key = VK_RETURN;
        else if (strcasecmp(name, "DELETE") == 0) key = VK_DELETE;
        else if (strcasecmp(name, "LEFT") == 0)   key = VK_LEFT;
        else if (strcasecmp(name, "UP") == 0)     key = VK_UP;
        else if (strcasecmp(name, "RIGHT") == 0)  key = VK_RIGHT;
        else if (strcasecmp(name, "DOWN") == 0)   key = VK_DOWN;
        else if (strcasecmp(name, "F1") == 0)     key = VK_F1;
        else if (strcasecmp(name, "F2") == 0)     key = VK_F2;
        else if (strcasecmp(name, "F3") == 0)     key = VK_F3;
        else if (strcasecmp(name, "F4") == 0)     key = VK_F4;
        else if (strcasecmp(name, "F5") == 0)     key = VK_F5;
        else if (strcasecmp(name, "F6") == 0)     key = VK_F6;
        else if (strcasecmp(name, "F7") == 0)     key = VK_F7;
        else if (strcasecmp(name, "F8") == 0)     key = VK_F8;
        else if (strcasecmp(name, "F9") == 0)     key = VK_F9;
        else if (strcasecmp(name, "F10") == 0)    key = VK_F10;
        else if (strcasecmp(name, "F11") == 0)    key = VK_F11;
        else if (strcasecmp(name, "F12") == 0)    key = VK_F12;
        else if (strcasecmp(name, "SPACE") == 0)  key = VK_SPACE;
        else
        {
            return result.SetFail("invalid key:%s", name);
        }
#else
        return result.SetFail("unsupported.");
#endif
        return result.SetSucc(key);
    }

public:
    std::string m_name;    //热键名称
    int m_mods;            //第一个按键，可以是组合键，比如CONTROL，ALT+SHIFT等
    int m_key;             //第二个按键，比如Q
    PCHotKeyCallBack m_func;//热键处理函数
};


#if defined(PCOS_WIN)
/**
*@brief     全局热键管理类
*           这是个类采用单例模式
*/
class CPCHotKey :CPCNoCopyable
{
public:
    explicit CPCHotKey()
        :m_tid(0)
    {}
    ~CPCHotKey()
    {}

    /**
    *@brief     单例
    */
    static CPCHotKey& obj()
    {
        static CPCHotKey o;
        return o;
    }

    /**
    *@brief     启动
    */
    CPCResult<> Start()
    {
        return CPCThreadPool::Default().AddTask(std::bind(&CPCHotKey::HotKeyMsgLoop, this));
    }

    /**
    *@brief     停止
    */
    void Stop()
    {
        ::PostThreadMessageA(m_tid, PC_WM_EXITHOTKEY, 0, 0);
    }

    /**
    *@brief     新注册一个热键
    *@param     keyname    [in]    热键名称，如ALT+Q CONTROL+1 SHIFT+ALT+Z
    *@param     callback   [in]    热键触发的回调函数
    *@return    是否成功，成功后IntResult()返回一个热键id，用于反注册
    */
    CPCResult<int> Register(const char* keyname, CPCHotKeyInfo::PCHotKeyCallBack callback)
    {
        CPCResult<int> result;
        std::unique_ptr<CPCHotKeyInfo> ptr(new CPCHotKeyInfo());
        auto result2 = ptr->Init(keyname, callback);
        if (!result2)
        {
            return result.SetFail("Init fail:%s",result2.ErrDesc());
        }
        int id = ptr->m_mods ^ ptr->m_key;
        if (m_hotkeys.end() != m_hotkeys.find(id))
        {
            return result.SetFail("conflict.");
        }
        m_hotkeys.insert(std::make_pair(id, std::move(ptr)));
        BOOL bRet = ::PostThreadMessageA(m_tid, PC_WM_ADDHOTKEY, id, 0);
        if (!bRet)
        {
            return result.SetSystemFail();
        }
        return result.SetSucc(id);
    }

    /**
    *@brief     反注册一个热键
    *@param     id    [in]    注册时返回的id
    *@return    是否成功
    */
    CPCResult<> UnRegister(int id)
    {
        CPCResult<> result;
        if (m_hotkeys.end() == m_hotkeys.find(id))
        {
            return result.SetFail("not find hot key.");
        }
        BOOL bRet = ::PostThreadMessageA(m_tid, PC_WM_DELHOTKEY, id, 0);
        if (!bRet)
        {
            return result.SetSystemFail();
        }
        return result;
    }

protected:
    void HotKeyMsgLoop()
    {
        m_tid = PCGetTID();
        MSG msg = { 0 };
        while (GetMessageA(&msg, NULL, 0, 0) != 0)
        {
            if (msg.message == WM_NULL)
                continue;
            if (msg.message == PC_WM_EXITHOTKEY)
                break;

            auto iter = m_hotkeys.find((int)msg.wParam);
            if (m_hotkeys.end() == iter)
            {
                //printf("not find hot key[%d] from hotkeys list. msg=%u\n", msg.wParam, msg.message);
                continue;
            }
            auto& ptr = iter->second;
            if (!ptr)
            {
                //printf("not  hot key[%d] info.msg=%u\n", msg.wParam, msg.message);
                continue;
            }

            if (msg.message == WM_HOTKEY)
            {
                if (ptr->m_func)
                    ptr->m_func(ptr->m_name);
                else
                    printf("hot key not define process function!\n");
            }
            else if (msg.message == PC_WM_ADDHOTKEY)
            {
                int id = ptr->m_mods^ptr->m_key;
                BOOL bSucc = ::RegisterHotKey(NULL, id, ptr->m_mods, ptr->m_key);
                if (!bSucc) printf("RegisterHotKey fail!\n");
            }
            else if (msg.message == PC_WM_DELHOTKEY)
            {
                int id = ptr->m_mods^ptr->m_key;
                BOOL bSucc = ::UnregisterHotKey(NULL, id);
                if (!bSucc) printf("UnregisterHotKey fail!\n");
            }
        }
    }

protected:
    std::unordered_map<int, std::unique_ptr<CPCHotKeyInfo> > m_hotkeys;
    int m_tid;
};

#else

/**
*@brief     全局热键管理类
*           这是个类采用单例模式
*/
class CPCHotKey :CPCNoCopyable
{
public:
    explicit CPCHotKey()
    {}
    ~CPCHotKey()
    {}

    /**
    *@brief     单例
    */
    static CPCHotKey& obj()
    {
        static CPCHotKey o;
        return o;
    }

    /**
    *@brief     启动
    */
    CPCResult<> Start()
    {
        CPCResult<> result;
        return result;
    }

    /**
    *@brief     停止
    */
    void Stop()
    {
    }

    /**
    *@brief     新注册一个热键
    *@param     keyname    [in]    热键名称，如ALT+Q CONTROL+1 SHIFT+ALT+Z
    *@param     callback   [in]    热键触发的回调函数
    *@return    是否成功，成功后IntResult()返回一个热键id，用于反注册
    */
    CPCResult<int> Register(const char* keyname, CPCHotKeyInfo::PCHotKeyCallBack callback)
    {
        CPCResult<int> result;
        return result.SetFail("unsupported");
    }
};

#endif








#endif    //___PCLIB_S_HOTKEY___